Skip to content

[feat] 업장 대표 이미지 캐러셀 UI 구현 및 API 연동#57

Open
ysw789 wants to merge 8 commits into
devfrom
feat/alt-266
Open

[feat] 업장 대표 이미지 캐러셀 UI 구현 및 API 연동#57
ysw789 wants to merge 8 commits into
devfrom
feat/alt-266

Conversation

@ysw789

@ysw789 ysw789 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

ID

  • ALT-266

변경 내용

  • 업장 대표 이미지 동적 표시: 사장님 홈 배너를 정적 이미지에서 업장의 대표 이미지로 변경
  • 이미지 캐러셀 UI: 업장의 모든 이미지를 보여주는 모달 추가
  • 이미지 관리 페이지: 이미지 추가/삭제/순서 변경/저장 기능 구현
  • 라우트 및 쿼리 설정: 새로운 이미지 편집 페이지 라우트, 이미지 조회 쿼리키 추가

구현 사항

새로운 기능 모듈 추가 (src/features/manager/workspace-image/)

  • API: workspaceImage.ts - 이미지 조회/업데이트 API 호출 로직
  • Hooks:
    • useWorkspaceImagesQuery - 업장 이미지 목록 조회
    • useUpdateWorkspaceImagesMutation - 이미지 수정 API 호출
    • useWorkspaceImageEditViewModel - 이미지 편집 화면 ViewModel (상태 관리)
  • UI Components:
    • WorkspaceImageCarousel - 모달 형태의 이미지 캐러셀 뷰어
  • Types & Constants: 이미지 타입 정의, 최대 이미지 수(5개) 등 상수 정의

이미지 편집 페이지 추가 (src/pages/manager/workspace-image-edit/)

  • 이미지 업로드, 삭제, 순서 변경, 대표 이미지 설정 기능
  • ViewModel 패턴으로 비즈니스 로직과 UI 분리

기존 코드 수정

  • App.tsx: 새로운 이미지 편집 페이지 라우트 등록
  • manager/home/index.tsx:
    • 정적 배너 이미지 제거, 동적 이미지로 변경
    • 배너 클릭 시 이미지 캐러셀(이미지 있을 경우) 또는 편집 페이지(이미지 없을 경우)로 이동
    • 이미지 캐러셀 모달 추가
  • routes.ts: WORKSPACE_IMAGES_EDIT_PATTERN 라우트 및 빌더 함수 추가
  • queryKeys.ts: 업장 이미지 조회 쿼리키 추가
  • appFileUpload.ts: WORKSPACE_REPRESENTATIVE_IMAGE 파일 업로드 타입 추가

참고 사항

시연

2026-06-24.8.46.10.mov

Summary by CodeRabbit

  • New Features

    • 관리자용 업장 대표 이미지 조회/수정 화면이 추가되었습니다.
    • 이미지 캐러셀과 편집 페이지에서 썸네일, 메인 이미지 지정, 추가/삭제, 저장 기능을 사용할 수 있습니다.
    • 업장 상세 화면의 배너가 고정 이미지 대신 업장 이미지 목록을 표시하도록 변경되었습니다.
  • Bug Fixes

    • 이미지가 없는 경우와 로드 실패 시 안내 및 재시도 동작이 보강되었습니다.
    • 이미지 개수, 형식, 용량 제한이 적용되었습니다.
  • Chores

    • 관리자 이미지 편집 경로와 관련 데이터 조회 갱신 흐름이 추가되었습니다.

@vercel

vercel Bot commented Jun 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
alter-client Ready Ready Preview, Comment Jun 29, 2026 2:10am

Request Review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ysw789, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 46 minutes and 47 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2bb9dfef-07e3-43e9-9759-f9d535d66e56

📥 Commits

Reviewing files that changed from the base of the PR and between 5a0cac1 and 25a8600.

📒 Files selected for processing (14)
  • src/app/App.tsx
  • src/features/manager/workspace-image/api/workspaceImage.ts
  • src/features/manager/workspace-image/constants/workspaceImage.ts
  • src/features/manager/workspace-image/hooks/useUpdateWorkspaceImagesMutation.ts
  • src/features/manager/workspace-image/hooks/useWorkspaceImageEditViewModel.ts
  • src/features/manager/workspace-image/hooks/useWorkspaceImagesQuery.ts
  • src/features/manager/workspace-image/index.ts
  • src/features/manager/workspace-image/types/workspaceImage.ts
  • src/features/manager/workspace-image/ui/WorkspaceImageCarousel.tsx
  • src/pages/manager/home/index.tsx
  • src/pages/manager/workspace-image-edit/index.tsx
  • src/shared/api/appFileUpload.ts
  • src/shared/constants/routes.ts
  • src/shared/lib/queryKeys.ts
📝 Walkthrough

Walkthrough

업장 대표 이미지를 조회·편집·저장하는 매니저 흐름이 추가되었습니다. 공유 경로, 쿼리키, 업로드 타입과 API/훅을 확장하고, 편집 뷰모델·캐러셀·편집 페이지를 구현한 뒤 홈 배너와 라우팅에 연결했습니다.

Changes

업장 대표 이미지 기능 추가

Layer / File(s) Summary
공유 계약 확장
src/shared/constants/routes.ts, src/shared/lib/queryKeys.ts, src/shared/api/appFileUpload.ts, src/features/manager/workspace-image/types/workspaceImage.ts, src/features/manager/workspace-image/constants/workspaceImage.ts
이미지 편집 경로, 쿼리키, 업로드 대상 타입, 이미지 DTO/요청·응답 타입, 업로드 제한 상수를 추가한다.
이미지 API와 조회/변경 훅
src/features/manager/workspace-image/api/workspaceImage.ts, src/features/manager/workspace-image/hooks/useWorkspaceImagesQuery.ts, src/features/manager/workspace-image/hooks/useUpdateWorkspaceImagesMutation.ts, src/features/manager/workspace-image/index.ts
대표 이미지 GET/PUT API와 조회·변경 훅을 추가하고, 저장 후 캐시 무효화를 연결하며 배럴 export를 갱신한다.
편집 뷰모델
src/features/manager/workspace-image/hooks/useWorkspaceImageEditViewModel.ts
서버 이미지를 로컬 편집 상태로 관리하고, 업로드·삭제·메인 지정·저장·정리 흐름을 제공한다.
대표 이미지 캐러셀
src/features/manager/workspace-image/ui/WorkspaceImageCarousel.tsx
전체화면 모달 캐러셀을 렌더링하고 키보드 내비게이션, 스크롤 잠금, 썸네일, 인디케이터를 처리한다.
이미지 편집 페이지
src/pages/manager/workspace-image-edit/index.tsx
workspaceId 검증 후 편집 UI를 렌더링하고, 로딩/오류/정상 상태와 저장·취소 동작을 구성한다.
홈 배너와 라우팅 연결
src/pages/manager/home/index.tsx, src/app/App.tsx
홈 배너를 이미지 기반으로 바꾸고 캐러셀/편집 페이지 이동을 연결하며, 앱 라우터에 편집 경로를 등록한다.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • alter-app/alter-client#7: src/pages/manager/home/index.tsx의 매니저 홈 영역을 직접 수정해 이번 배너 통합과 같은 화면 경로를 건드립니다.
  • alter-app/alter-client#42: src/shared/api/appFileUpload.ts의 업로드 계약을 다뤄, 이번 PR의 WORKSPACE_REPRESENTATIVE_IMAGE 타겟 추가와 직접 연결됩니다.
  • alter-app/alter-client#55: 동일한 uploadAppFile 경로를 조정한 PR로, 이번 PR이 그 업로드 API에 새 대상 타입을 추가합니다.

Suggested reviewers

  • kim3360
  • dohy-eon
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 업장 대표 이미지 캐러셀 UI와 API 연동을 핵심으로 잘 요약해 변경 사항과 일치합니다.
Description check ✅ Passed ID, 변경 내용, 구현 사항, 시연, 참고 사항이 모두 포함되어 템플릿 요구사항을 대부분 충족합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/alt-266

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ysw789 ysw789 marked this pull request as ready for review June 24, 2026 11:41
@ysw789 ysw789 self-assigned this Jun 24, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/manager/workspace-image-edit/index.tsx`:
- Around line 136-140: The representative image in the workspace image editor is
currently treated as decorative because the <img> inside the workspace image
tile uses an empty alt. Update the image in the workspace-image-edit component
so each tile gets meaningful index-based alt text (for example, identifying its
position or order) rather than an empty string, using the existing image
rendering logic around image.url.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8fd1bbc3-95cc-4c64-a9c6-015e674eded6

📥 Commits

Reviewing files that changed from the base of the PR and between 8a62e58 and b0a739a.

📒 Files selected for processing (14)
  • src/app/App.tsx
  • src/features/manager/workspace-image/api/workspaceImage.ts
  • src/features/manager/workspace-image/constants/workspaceImage.ts
  • src/features/manager/workspace-image/hooks/useUpdateWorkspaceImagesMutation.ts
  • src/features/manager/workspace-image/hooks/useWorkspaceImageEditViewModel.ts
  • src/features/manager/workspace-image/hooks/useWorkspaceImagesQuery.ts
  • src/features/manager/workspace-image/index.ts
  • src/features/manager/workspace-image/types/workspaceImage.ts
  • src/features/manager/workspace-image/ui/WorkspaceImageCarousel.tsx
  • src/pages/manager/home/index.tsx
  • src/pages/manager/workspace-image-edit/index.tsx
  • src/shared/api/appFileUpload.ts
  • src/shared/constants/routes.ts
  • src/shared/lib/queryKeys.ts

Comment thread src/pages/manager/workspace-image-edit/index.tsx

@dohy-eon dohy-eon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다 나머진 다 좋아요~~

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요거 성능 최적화만 하면 좋을거 같아요. 어차피 모달 닫혀있으면 렌더링이 안되는 구조라서 호출부(manager/home/index.tsx)에서 조건부 렌더링(isOpen && <WorkspaceImageCarousel ... />)을 적용하면 내부에서 wasOpen 같은 상태처리 안해도 될거 같네여?

@ysw789

ysw789 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

수고하셨습니다 나머진 다 좋아요~~

따봉

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/features/manager/workspace-image/ui/WorkspaceImageCarousel.tsx (1)

32-50: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

이미지가 0개가 되면 모달이 “닫히지” 않고 전역 부작용만 남습니다.

total === 0 가드가 useEffect 뒤에 있어서, 열려 있는 동안 images가 빈 배열로 바뀌면 스크롤 락과 keydown 리스너가 유지된 채 컴포넌트만 null을 반환합니다. 지금 구조에서는 보이지 않는 모달 때문에 페이지 스크롤이 막힌 상태가 남을 수 있습니다. total === 0일 때는 전역 부작용을 걸지 말고 바로 onClose()로 닫는 쪽이 안전합니다.

수정 예시
  useEffect(() => {
+    if (total === 0) {
+      onClose()
+      return
+    }
+
     const previousOverflow = document.body.style.overflow
     document.body.style.overflow = 'hidden'

     const handleKeyDown = (event: KeyboardEvent) => {
       if (event.key === 'Escape') onClose()
       if (event.key === 'ArrowLeft') goPrev()
       if (event.key === 'ArrowRight') goNext()
     }
     window.addEventListener('keydown', handleKeyDown)

     return () => {
       document.body.style.overflow = previousOverflow
       window.removeEventListener('keydown', handleKeyDown)
     }
-  }, [onClose, goPrev, goNext])
+  }, [total, onClose, goPrev, goNext])

   if (total === 0) return null
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/manager/workspace-image/ui/WorkspaceImageCarousel.tsx` around
lines 32 - 50, The WorkspaceImageCarousel modal can return null when total
becomes 0, but the useEffect still applies body scroll lock and the keydown
listener, leaving global side effects behind. Move the empty-state handling so
that when total === 0 the component closes via onClose() immediately and does
not install the effect, or guard the effect itself to skip setup in that case.
Use the WorkspaceImageCarousel component, the total check, and the existing
onClose/goPrev/goNext handlers to ensure the modal fully cleans up when the
image list becomes empty.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/features/manager/workspace-image/ui/WorkspaceImageCarousel.tsx`:
- Around line 32-50: The WorkspaceImageCarousel modal can return null when total
becomes 0, but the useEffect still applies body scroll lock and the keydown
listener, leaving global side effects behind. Move the empty-state handling so
that when total === 0 the component closes via onClose() immediately and does
not install the effect, or guard the effect itself to skip setup in that case.
Use the WorkspaceImageCarousel component, the total check, and the existing
onClose/goPrev/goNext handlers to ensure the modal fully cleans up when the
image list becomes empty.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8685f62b-a2ff-4c4e-9df1-29cfb9e50b40

📥 Commits

Reviewing files that changed from the base of the PR and between b0a739a and 5a0cac1.

📒 Files selected for processing (2)
  • src/features/manager/workspace-image/ui/WorkspaceImageCarousel.tsx
  • src/pages/manager/home/index.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/manager/home/index.tsx

ysw789 added 7 commits June 29, 2026 11:05
- useWorkspaceImagesQuery에서 refetch 노출
- useWorkspaceImageEditViewModel에서 조회 성공 시에만 초기화하도록 가드 강화
  (실패 시 빈 목록으로 잠겨 전체 교체 저장으로 이미지 삭제되는 경로 제거)
- 편집 페이지에 load-error 상태 추가: 에러 UI + 다시 시도 버튼 + 저장 비활성
- isLoadError 파생값으로 초기 로드 실패 상태 구분
- totalCount 중복 상태 제거, items.length 사용
- Spinner 추가, close/plus 아이콘 인라인 SVG로 교체
- safeIndex = Math.min(index, total-1)로 단일 클램프값 도입
  (active 이미지, 카운터, 썸네일 하이라이트, 점 인디케이터 4곳 통일)
- 이미지 길이 변경 시에도 UI 동기화 보장
- text-[18px] → text-5, text-[22px] → text-7 토큰 적용
- 유니코드 글리프(‹/›) → ChevronLeft/RightIcon SVG 컴포넌트 사용
- close(✕)/edit(✎) 글리프 → 인라인 currentColor SVG로 교체
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants